﻿
using Boilen.Primitives.Members;
using System.Collections.Generic;
using System.Linq;
using System.Windows;


namespace Boilen.Primitives.Implementers {

    /// <inheritdoc/>
    /// <summary>
    /// Implements an named template part.
    /// </summary>
    public sealed class TemplatePart<T> : ValueAccessor<T> where T : class {

        internal const string DescriptionFormat = "Gets {0}.";


        /// <summary>
        /// Gets the part name of the template part.
        /// </summary>
        public string PartName { get { return "PART_" + this.AccessorName; } }

        /// <summary>
        /// Gets the name of the initialization helper method.
        /// </summary>
        private string HelperName { get { return "Initialize" + this.AccessorName; } }


        /// <inheritdoc/>
        protected override IEnumerable<InitializationMember> Initializers {
            get {
                var oldValueInitializer = CreateInitializer( false, "{0} old{1} = this.{1}", this.TypeName, this.AccessorName );
                var partInitializer = CreateInitializer( true, "this.GetTemplateChild(\"{0}\") as {1}", this.PartName, this.TypeName );
                var helperCall = CreateInitializer( false, "this.{0}(old{1}, this.{1})", this.HelperName, this.AccessorName );

                oldValueInitializer.DependentInitializer = partInitializer;
                partInitializer.DependentInitializer = helperCall;

                yield return oldValueInitializer;
            }
        }

        /// <inheritdoc/>
        protected override AccessorMember Accessor {
            get {
                var oldValue = new ParameterMember( "old" + this.AccessorName, this.TypeName );
                var newValue = new ParameterMember( "new" + this.AccessorName, this.TypeName );
                var helper = new MethodMember( this.HelperName, "void", null ) {
                    Parameters = { oldValue, newValue },
                    Attributes = { AttributeMember.SuppressUnusedPartialParameters }
                };

                if( GlobalSettings.CodeAnalysisRuleNamespace != null && GlobalSettings.WeakEventCodeAnalysisRule != null )
                    helper.Attributes.Add( AttributeMember.SuppressMessage(
                        GlobalSettings.CodeAnalysisRuleNamespace,
                        GlobalSettings.WeakEventCodeAnalysisRule,
                        "Template parts are guaranteed to be local to the control." ) );

                return new AccessorMember( this.AccessorName, this.TypeName ) {
                    Doc = this.CreateDoc( )
                        .AddSummary( DescriptionFormat, this.Description ),
                    Modifiers = this.AccessorModifiers,
                    ObserveMember = this.CreateAccessorBlock(
                        ObserveAccessorName,
                        string.Format( "return this.{0};", this.FieldName )
                    ),
                    Helpers = { helper }
                };
            }
        }

        private AttributeMember PartAttribute {
            get {
                var typeRepository = this.Parent.TypeRepository;
                string templatePartAttributeTypeName = typeRepository.GetTypeName( typeof( TemplatePartAttribute ) );
                string nameValue = typeRepository.GetValueString( this.PartName, false );
                string typeValue = typeRepository.GetValueString( this.Type, false );

                var partAttribute = AttributeMember.Custom( templatePartAttributeTypeName );
                partAttribute.NamedArguments.Add( "Name", nameValue );
                partAttribute.NamedArguments.Add( "Type", typeValue );

                return partAttribute;
            }
        }


        /// <inheritdoc/>
        public TemplatePart( PartialType parent, string name, string description )
            : base( parent, name, description ) {
            this.Accessibility = Accessibility.Private;
        }


        /// <inheritdoc/>
        protected override IEnumerable<Member> GetMembers( ) {
            var partAttribute = this.PartAttribute;
            Ensure.NotNull( partAttribute );

            return base.GetMembers( )
                .Concat( new[] { partAttribute } );
        }


        private InitializationMember CreateInitializer( bool writeAssignment, string format, params object[] args ) {
            return new InitializationMember( this.FieldName, string.Format( format, args ) ) {
                MemberScope = InitializationMember.OnApplyTemplateMemberScope,
                WriteVariableAssignment = writeAssignment
            };
        }

    }

}
